package com.swt.gap.common.graph;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.swt.gap.common.constant.CommonConstant;
import com.swt.gap.common.constant.GraphDbConstant;
import com.swt.gap.common.constant.RedisConstant;
import com.swt.gap.config.ServerConfig;
import com.swt.gap.dao.BankCardBinDao;
import com.swt.gap.dao.OntologyNodeDao;
import com.swt.gap.dao.OntologyNodePropertyDao;
import com.swt.gap.dao.SysIconMapper;
import com.swt.gap.model.entity.BankCardBin;
import com.swt.gap.model.entity.OntologyNode;
import com.swt.gap.model.entity.OntologyNodeProperty;
import com.swt.gap.service.TaskGraphData;
import com.swt.gap.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.schema.Schema;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author sx
 */
@Slf4j
@Component
public class GraphProcessor {

    private final EmbeddedNeo4j embeddedNeo4j;
    private final SysIconMapper sysIconMapper;
    private final ServerConfig serverConfig;
    private final BankCardBinDao bankCardBinDao;
    private final RedisUtil redisUtil;
    private final TaskGraphData taskGraphData;
    private final OntologyNodePropertyDao ontologyNodePropertyDao;
    private final Neo4jTools neo4jTools;
    private final OntologyNodeDao ontologyNodeDao;


    public GraphProcessor(EmbeddedNeo4j embeddedNeo4j, SysIconMapper sysIconMapper,
                          ServerConfig serverConfig, BankCardBinDao bankCardBinDao,
                          RedisUtil redisUtil,
                          TaskGraphData taskGraphData,
                          OntologyNodePropertyDao ontologyNodePropertyDao,
                          Neo4jTools neo4jTools,
                          OntologyNodeDao ontologyNodeDao) {
        this.embeddedNeo4j = embeddedNeo4j;
        this.sysIconMapper = sysIconMapper;
        this.serverConfig = serverConfig;
        this.bankCardBinDao = bankCardBinDao;
        this.redisUtil = redisUtil;
        this.taskGraphData = taskGraphData;
        this.ontologyNodePropertyDao = ontologyNodePropertyDao;
        this.neo4jTools = neo4jTools;
        this.ontologyNodeDao = ontologyNodeDao;
    }



    public GraphNeo4jVO getNodeByCypher(String cypher) {
        Set<GraphNeo4jVO.Node> retNodes = new LinkedHashSet<>();
        GraphDatabaseService graphDatabaseService = embeddedNeo4j.getGraphDatabaseService();
        try (Transaction tx = graphDatabaseService.beginTx()) {
            Result result = graphDatabaseService.execute(cypher);
            result.forEachRemaining(res -> {
                Node node = (Node) res.get("n");
                GraphNeo4jVO.Node retNode = transferDbNodeToCustomNode(node);
                retNodes.add(retNode);
            });
            tx.success();
            return new GraphNeo4jVO(new ArrayList<>(retNodes), null);
        }
    }

    public GraphNeo4jVO getPathByCypher(String cypher) {
        Set<GraphNeo4jVO.Node> retNodes = new LinkedHashSet<>();
        List<GraphNeo4jVO.Link> retLinks = new ArrayList<>();
        GraphDatabaseService graphDatabaseService = embeddedNeo4j.getGraphDatabaseService();
        try (Transaction tx = graphDatabaseService.beginTx()) {
            Result result = graphDatabaseService.execute(cypher);
            result.forEachRemaining(res -> {
                Path path = (Path) res.get("p");
                Iterable<Node> nodes = path.nodes();
                nodes.forEach(node -> {
                    GraphNeo4jVO.Node retNode = transferDbNodeToCustomNode(node);
                    retNodes.add(retNode);
                });
                Iterable<Relationship> relationships = path.relationships();
                relationships.forEach(relationship -> {
                    GraphNeo4jVO.Link link = new GraphNeo4jVO.Link();
                    link.setId(relationship.getProperty(GraphDbConstant.PROPERTY_KEY_ID).toString());
                    link.setStartNodeId(relationship.getStartNode().getProperty(GraphDbConstant.PROPERTY_KEY_ID).toString());
                    link.setEndNodeId(relationship.getEndNode().getProperty(GraphDbConstant.PROPERTY_KEY_ID).toString());
                    Map<String, Object> relProperties = relationship.getAllProperties();
                    relProperties.forEach((k, v) -> {
                        if (GraphDbConstant.PROPERTY_KEY_SOURCE.equals(k)) {
                            Set<JSONObject> newValue = new HashSet<>();
                            JSONArray jaValue = JSON.parseArray((String) v);
                            for (Object o : jaValue) {
                                newValue.add((JSONObject) o);
                            }
                            relProperties.put(k, newValue);
                        } else if (GraphDbConstant.PROPERTY_KEY_TIMES.equals(k)) {
                            relProperties.put(k, JSON.parseArray((String) v));
                        }
                    });
                    link.setProperties(relProperties);
                    link.setType(relationship.getType().name());
//                    link.setColor(relationship.getProperty(GraphDbConstant.PROPERTY_KEY_COLOR).toString());
//                    link.setWidth(relationship.getProperty(GraphDbConstant.PROPERTY_KEY_WIDTH).toString());
                    retLinks.add(link);
                });
            });
            return new GraphNeo4jVO(new ArrayList<>(retNodes), retLinks);
        }
    }

    public GraphNeo4jVO getPathByCypher2(String cypher) {
        Set<GraphNeo4jVO.Node> retNodes = new LinkedHashSet<>();
        List<GraphNeo4jVO.Link> retLinks = new ArrayList<>();
        GraphDatabaseService graphDatabaseService = embeddedNeo4j.getGraphDatabaseService();
        try (Transaction tx = graphDatabaseService.beginTx()) {
            Result result = graphDatabaseService.execute(cypher);

            result.forEachRemaining(res -> {
                res.forEach((key, value) -> {
                    Path path = (Path) value;
                    Iterable<Node> nodes = path.nodes();
                    nodes.forEach(node -> {
                        GraphNeo4jVO.Node retNode = transferDbNodeToCustomNode(node);
                        retNodes.add(retNode);
                    });
                    Iterable<Relationship> relationships = path.relationships();
                    relationships.forEach(relationship -> {
                        GraphNeo4jVO.Link link = new GraphNeo4jVO.Link();
                        link.setId(relationship.getProperty(GraphDbConstant.PROPERTY_KEY_ID).toString());
                        link.setStartNodeId(relationship.getStartNode().getProperty(GraphDbConstant.PROPERTY_KEY_ID).toString());
                        link.setEndNodeId(relationship.getEndNode().getProperty(GraphDbConstant.PROPERTY_KEY_ID).toString());
                        Map<String, Object> relProperties = relationship.getAllProperties();
                        if (null != relProperties) {
                            relProperties.forEach((k, v) -> {
                                if (GraphDbConstant.PROPERTY_AMOUNT.equals(k)) {
                                    String amount = v.toString();
                                    BigDecimal bd = new BigDecimal(amount);
                                    relProperties.put(k, bd.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
                                } else if (GraphDbConstant.PROPERTY_KEY_SOURCE.equals(k)) {
                                    Set<JSONObject> newValue = new HashSet<>();
                                    JSONArray jaValue = JSON.parseArray((String) v);
                                    for (Object o : jaValue) {
                                        newValue.add((JSONObject) o);
                                    }
                                    relProperties.put(k, newValue);
                                } else if (GraphDbConstant.PROPERTY_KEY_TIMES.equals(k)) {
                                    relProperties.put(k, JSON.parseArray((String) v));
                                } else if (GraphDbConstant.PROPERTY_MERGE_TYPE.equals(k)) {
                                    link.setMergeType((String) v);
                                }
                            });
                            relProperties.remove(GraphDbConstant.PROPERTY_MERGE_TYPE);
                        }
                        link.setProperties(relProperties);
                        link.setType(relationship.getType().name());
                        retLinks.add(link);
                    });
                });
            });
            return new GraphNeo4jVO(new ArrayList<>(retNodes), retLinks);
        }
    }

    public Long executeReadCypherReturnNum(String cypher) {
        AtomicReference<Long> retValue = new AtomicReference<>(0L);
        GraphDatabaseService graphDatabaseService = embeddedNeo4j.getGraphDatabaseService();
        try (Transaction tx = graphDatabaseService.beginTx()) {
            Result result = graphDatabaseService.execute(cypher);
            result.forEachRemaining(res -> {
                retValue.set((Long) res.get("num"));
            });
        }
        return retValue.get();
    }

    /**
     * 根据cypher语句获取 neo4j定义格式的 路径
     *
     * @param cypher 必需返回startNode，endNode，relationship
     * @return List<Neo4jPath>
     */
    public List<Neo4jPath> getNeo4jPathByCypher(String cypher) {
        List<Neo4jPath> retValue = new ArrayList<>();
        GraphDatabaseService graphDatabaseService = embeddedNeo4j.getGraphDatabaseService();
        try (Transaction tx = graphDatabaseService.beginTx()) {
            Result result = graphDatabaseService.execute(cypher);
            result.forEachRemaining(res -> {
                GraphNeo4jVO.Node retStartNode = new GraphNeo4jVO.Node();
                Node startNode = (Node) res.get("startNode");
                retStartNode.setProperties(startNode.getAllProperties());
                List<String> retLabels = new ArrayList<>();
                startNode.getLabels().forEach(label -> {
                    retLabels.add(label.name());
                });
                retStartNode.setLabels(retLabels);

                GraphNeo4jVO.Node retEndNode = new GraphNeo4jVO.Node();
                Node endNode = (Node) res.get("endNode");
                retEndNode.setProperties(endNode.getAllProperties());
                List<String> retLabels2 = new ArrayList<>();
                endNode.getLabels().forEach(label -> {
                    retLabels2.add(label.name());
                });
                retEndNode.setLabels(retLabels2);

                GraphNeo4jVO.Link retRelationship = new GraphNeo4jVO.Link();
                Relationship relationship = (Relationship) res.get("relationship");
                retRelationship.setType(relationship.getType().name());
                retRelationship.setProperties(relationship.getAllProperties());
                Neo4jPath neo4jPath = new Neo4jPath(retStartNode, retEndNode, retRelationship);
                retValue.add(neo4jPath);
            });
        }
        return retValue;
    }

    public List<String> getAllLabelsInUse() {
        List<String> retValue = new ArrayList<>();
        Set<GraphNeo4jVO.Node> retNodes = new LinkedHashSet<>();
        GraphDatabaseService graphDatabaseService = embeddedNeo4j.getGraphDatabaseService();
        try (Transaction tx = graphDatabaseService.beginTx()) {
            graphDatabaseService.getAllLabelsInUse().forEach(label -> {
                retValue.add(label.name());
            });
        }
        return retValue;
    }

    public Set<String> getLabelsByCypher(String cypher) {
        Set<String> retValue = new HashSet<>();
        Set<GraphNeo4jVO.Node> retNodes = new LinkedHashSet<>();
        GraphDatabaseService graphDatabaseService = embeddedNeo4j.getGraphDatabaseService();
        try (Transaction tx = graphDatabaseService.beginTx()) {
            Result result = graphDatabaseService.execute(cypher);
            result.forEachRemaining(res -> {
                List<String> labels = (List<String>) res.get("labels");
                retValue.addAll(labels);
            });
        }
        return retValue;
    }

    public Set<String> getKeysByCypher(String cypher) {
        Set<String> retValue = new HashSet<>();
        Set<GraphNeo4jVO.Node> retNodes = new LinkedHashSet<>();
        GraphDatabaseService graphDatabaseService = embeddedNeo4j.getGraphDatabaseService();
        try (Transaction tx = graphDatabaseService.beginTx()) {
            Result result = graphDatabaseService.execute(cypher);
            result.forEachRemaining(res -> {
                List<String> labels = (List<String>) res.get("keys");
                retValue.addAll(labels);
            });
        }
        return retValue;
    }

    /**
     * neo4j的节点转换成前端能显示的节点
     * @param   node
     * @return GraphNeo4jVO.Node
     */
    public GraphNeo4jVO.Node transferDbNodeToCustomNode(Node node) {
        GraphNeo4jVO.Node retNode = new GraphNeo4jVO.Node();

        StringBuilder sbLabels = new StringBuilder();
        List<String> retLabels = new ArrayList<>();
        node.getLabels().forEach(label -> {
            retLabels.add(label.name());
            sbLabels.append(label).append(",");
        });
        retNode.setLabels(retLabels);
        retNode.setId(node.getProperty(GraphDbConstant.PROPERTY_KEY_ID).toString());

        List<OntologyNodeProperty> labelProperties = ontologyNodePropertyDao
                .listAllPropertyByLabels(StringUtils.arrayToCommaDelimitedString(retLabels.toArray()));
        if (CollectionUtils.isEmpty(labelProperties) || null == labelProperties.get(0)) {
            List<OntologyNode> ontologyNodes = ontologyNodeDao
                    .selectByLabelsWithDeleted(StringUtils.arrayToCommaDelimitedString(retLabels.toArray()));
            if (!CollectionUtils.isEmpty(ontologyNodes)) {
                labelProperties = ontologyNodePropertyDao.selectByEntityId(ontologyNodes.get(0).getNodeId());
            }
        }
        List<String> displayValues = new ArrayList<>();
        List<String> primaryValues = new ArrayList<>();
        labelProperties.forEach(prop -> {
            Optional.ofNullable(prop).ifPresent(p -> {
                String value = "";
                if (node.getAllProperties().containsKey(p.getPropertyKey())) {
                    value = node.getProperty(p.getPropertyKey()).toString();
                }
                if (p.getDisplay() && !StringUtils.isEmpty(value)) {
                    displayValues.add(value);
                }
                if (p.getPrimaryKey() && !StringUtils.isEmpty(value)) {
                    primaryValues.add(value);
                }
            });

        });
        if (displayValues.size() > 0) {
            retNode.setName(StringUtils.arrayToCommaDelimitedString(displayValues.toArray()));
        } else if (!primaryValues.isEmpty()){
            retNode.setName(primaryValues.get(0));
        }

        String iconFile = "";
        if (node.getAllProperties().containsKey(GraphDbConstant.PROPERTY_KEY_ICON) &&
                node.getProperty(GraphDbConstant.PROPERTY_KEY_ICON).toString().contains(".")) {
            iconFile = node.getProperty(GraphDbConstant.PROPERTY_KEY_ICON).toString();
            if (iconFile.contains("upload/")) {
                iconFile = iconFile.substring(iconFile.indexOf("upload/") + "upload/".length());
                retNode.setIcon(serverConfig.parseViewUrl(iconFile, 1));
            }
        } else if (retLabels.contains("国家") || retLabels.contains("装备")|| retLabels.contains("人物")) {
//            String name = "";
//            if (node.getAllProperties().containsKey(GraphDbConstant.PROPERTY_KEY_NAME)) {
//                name = node.getProperty(GraphDbConstant.PROPERTY_KEY_NAME).toString();
//            } else if (node.getAllProperties().containsKey(GraphDbConstant.PROPERTY_NAME)) {
//                name = node.getProperty(GraphDbConstant.PROPERTY_NAME).toString();
//            }
            if (!primaryValues.isEmpty() && new File(CommonConstant.ICON_PATH + primaryValues.get(0) + ".png").isFile()) {
                retNode.setIcon(serverConfig.parseViewUrl(primaryValues.get(0) + ".png", 3));
            } else {
                iconFile = sysIconMapper.getIconFileByLabels(sbLabels.toString().substring(0, sbLabels.lastIndexOf(",")));
                retNode.setIcon(serverConfig.parseViewUrl(iconFile, 3));
            }
        } else if (sbLabels.toString().contains("卡号") || sbLabels.toString().contains("账号")
                || sbLabels.toString().contains("银行")) {
//            String name = "";
//            if (node.getAllProperties().containsKey(GraphDbConstant.PROPERTY_KEY_NAME)) {
//                name = node.getProperty(GraphDbConstant.PROPERTY_KEY_NAME).toString();
//            } else if (node.getAllProperties().containsKey(GraphDbConstant.PROPERTY_NAME)) {
//                name = node.getProperty(GraphDbConstant.PROPERTY_NAME).toString();
//            }
            String bankIconFile = "";
            if (!primaryValues.isEmpty() && primaryValues.get(0).length() > 5) {
                List<BankCardBin> bankCardBins = bankCardBinDao.selectByBin(primaryValues.get(0).substring(0, 6));
                if (!bankCardBins.isEmpty()) {
                    bankIconFile = bankCardBins.get(0).getIconFile();
                }
            }
            if (!StringUtils.isEmpty(bankIconFile)) {
                retNode.setIcon(serverConfig.parseViewUrl(bankIconFile, 3));
            } else {
                iconFile = sysIconMapper.getIconFileByLabels(sbLabels.toString().substring(0, sbLabels.lastIndexOf(",")));
                retNode.setIcon(serverConfig.parseViewUrl(iconFile, 3));
            }
        } else {
            iconFile = sysIconMapper.getIconFileByLabels(sbLabels.toString().substring(0, sbLabels.lastIndexOf(",")));
            retNode.setIcon(serverConfig.parseViewUrl(iconFile, 3));
        }

        Map<String, Object> nodeProperties = node.getAllProperties();
        if (node.getAllProperties().containsKey(GraphDbConstant.PROPERTY_KEY_SOURCE)) {
            Set<JSONObject> newValue = new HashSet<>();
            JSONArray jaValue = JSON.parseArray((String) nodeProperties.get(GraphDbConstant.PROPERTY_KEY_SOURCE));
            for (Object o : jaValue) {
                newValue.add((JSONObject) o);
            }
            nodeProperties.put(GraphDbConstant.PROPERTY_KEY_SOURCE, newValue);
        }
        if (retLabels.contains("地点") || retLabels.contains("军事基地") || retLabels.contains("港口")) {
            nodeProperties.put("地点", retNode.getName());
        }
        retNode.setProperties(nodeProperties);

        return retNode;
    }

    public Graph.Node transferDbNodeToNode(Node node) {
        Graph.Node retNode = new Graph.Node();

        StringBuilder sbLabels = new StringBuilder();
        List<String> retLabels = new ArrayList<>();
        node.getLabels().forEach(label -> {
            retLabels.add(label.name());
            sbLabels.append(label).append(",");
        });
        retNode.setLabels(retLabels);
        retNode.setId(node.getProperty(GraphDbConstant.PROPERTY_KEY_ID).toString());

        String iconFile = "";
        if (node.getAllProperties().containsKey(GraphDbConstant.PROPERTY_KEY_ICON) &&
                node.getProperty(GraphDbConstant.PROPERTY_KEY_ICON).toString().contains(".")) {
            iconFile = node.getProperty(GraphDbConstant.PROPERTY_KEY_ICON).toString();
            if (iconFile.contains("upload/")) {
                iconFile = iconFile.substring(iconFile.indexOf("upload/") + "upload/".length());
                retNode.setIcon(serverConfig.parseViewUrl(iconFile, 1));
            }
        } else if (retLabels.contains("国家") || retLabels.contains("装备")) {
            String name = "";
            if (node.getAllProperties().containsKey(GraphDbConstant.PROPERTY_KEY_NAME)) {
                name = node.getProperty(GraphDbConstant.PROPERTY_KEY_NAME).toString();
            } else if (node.getAllProperties().containsKey(GraphDbConstant.PROPERTY_NAME)) {
                name = node.getProperty(GraphDbConstant.PROPERTY_NAME).toString();
            }
            File file = new File(CommonConstant.ICON_PATH + name + ".png");
            if (file.exists()) {
                retNode.setIcon(serverConfig.parseViewUrl(name + ".png", 3));
            } else {
                iconFile = sysIconMapper.getIconFileByLabels(sbLabels.toString().substring(0, sbLabels.lastIndexOf(",")));
                retNode.setIcon(serverConfig.parseViewUrl(iconFile, 3));
            }
        } else if (sbLabels.toString().contains("卡号") || sbLabels.toString().contains("账号")) {
            String name = "";
            if (node.getAllProperties().containsKey(GraphDbConstant.PROPERTY_KEY_NAME)) {
                name = node.getProperty(GraphDbConstant.PROPERTY_KEY_NAME).toString();
            } else if (node.getAllProperties().containsKey(GraphDbConstant.PROPERTY_NAME)) {
                name = node.getProperty(GraphDbConstant.PROPERTY_NAME).toString();
            }
            String bankIconFile = "";
            if (name.length() > 5) {
                List<BankCardBin> bankCardBins = bankCardBinDao.selectByBin(name.substring(0, 6));
                if (!bankCardBins.isEmpty()) {
                    bankIconFile = bankCardBins.get(0).getIconFile();
                }
            }
//            File file = new File(CommonConstant.ICON_PATH + name + ".png");
            if (!StringUtils.isEmpty(bankIconFile)) {
                retNode.setIcon(serverConfig.parseViewUrl(bankIconFile, 3));
            } else {
                iconFile = sysIconMapper.getIconFileByLabels(sbLabels.toString().substring(0, sbLabels.lastIndexOf(",")));
                retNode.setIcon(serverConfig.parseViewUrl(iconFile, 3));
            }
        } else {
            iconFile = sysIconMapper.getIconFileByLabels(sbLabels.toString().substring(0, sbLabels.lastIndexOf(",")));
            retNode.setIcon(serverConfig.parseViewUrl(iconFile, 3));
        }
//        retNode.setAmount(0d);
//        if (node.getAllProperties().containsKey(GraphDbConstant.PROPERTY_AMOUNT)) {
//            retNode.setAmount(Double.parseDouble(node.getProperty(GraphDbConstant.PROPERTY_AMOUNT).toString()));
//        }
        Map<String, Object> nodeProperties = node.getAllProperties();
        if (node.getAllProperties().containsKey(GraphDbConstant.PROPERTY_KEY_SOURCE)) {
            Set<JSONObject> newValue = new HashSet<>();
            JSONArray jaValue = JSON.parseArray((String) nodeProperties.get(GraphDbConstant.PROPERTY_KEY_SOURCE));
            for (Object o : jaValue) {
                newValue.add((JSONObject) o);
            }
            nodeProperties.put(GraphDbConstant.PROPERTY_KEY_SOURCE, newValue);
        }
        retNode.setProperties(nodeProperties);

        return retNode;
    }



    public void createIndex(String label, String property) {
        GraphDatabaseService graphDatabaseService = embeddedNeo4j.getGraphDatabaseService();
        try (Transaction tx = graphDatabaseService.beginTx()) {
            Schema schema = graphDatabaseService.schema();
            schema.indexFor(Label.label(label))
                    .on(property)
                    .withName(label + "_" + property)
                    .create();
            tx.success();
        } catch (ConstraintViolationException e) {
            log.error(e.getMessage());
        }
    }


    public void createUniqueConstraint(String label, String property) {
        GraphDatabaseService graphDatabaseService = embeddedNeo4j.getGraphDatabaseService();
        try (Transaction tx = graphDatabaseService.beginTx()) {
            graphDatabaseService.schema()
                    .constraintFor(Label.label(label))
                    .assertPropertyIsUnique(property)
                    .create();
            tx.success();
        } catch (ConstraintViolationException e) {
            log.error(e.getMessage());
        }
    }


    public List<RelEventNode> getRelEventNodeByCypher(String cypher) {
        List<RelEventNode> retNodes = new ArrayList<>();
        GraphDatabaseService graphDatabaseService = embeddedNeo4j.getGraphDatabaseService();
        try (Transaction tx = graphDatabaseService.beginTx()) {
            Result result = graphDatabaseService.execute(cypher);
            result.forEachRemaining(res -> {
                RelEventNode relEventNode = new RelEventNode();
                Node node = (Node) res.get("n");
                GraphNeo4jVO.Node retNode = transferDbNodeToCustomNode(node);
                BeanUtils.copyProperties(retNode, relEventNode);
//                //中间关联节点
//                Node nodeM = (Node) res.get("m");
//                GraphNeo4jVO.Node retNodeM = transferDbNodeToCustomNode(node);
//                List<String> labels = new ArrayList<>();
//                node.getLabels().forEach(label -> {
//                    labels.add(label.name());
//                });
//                List<String> relNodeNames = new ArrayList<>();
//                relNodeNames.add("同" + labels.get(1) + "-" + retNodeM.getName());
//                relEventNode.setRelNodeNames(relNodeNames);
                retNodes.add(relEventNode);
            });
            return retNodes;
        }
    }


    public Boolean saveToEmbeddedNeo4j(Graph graphData, Integer taskId) {
        GraphDatabaseService graphDatabaseService = embeddedNeo4j.getGraphDatabaseService();
        final int[] num = {1};
        try (Transaction tx = graphDatabaseService.beginTx()) {
            Map<String, Node> mapNodes = new HashMap<>();
            graphData.getNodes().forEach(nodeData -> {

                Node existNode = null;
                StopWatch stopwatch = StopWatch.createStarted();
                ResourceIterator<Node> nodes = graphDatabaseService.findNodes(Label.label(nodeData.getLabels().get(1)),
                        GraphDbConstant.PROPERTY_KEY_ID, nodeData.getId());
                System.out.println("查找节点耗时： " + stopwatch.getTime(TimeUnit.MICROSECONDS));
                if (nodes.hasNext()) {
                    existNode = nodes.next();
                }
                if (null == existNode) {
                    Label[] labels = new Label[nodeData.getLabels().size()];
                    for (int i = 0; i < nodeData.getLabels().size(); i++) {
                        labels[i] = Label.label(nodeData.getLabels().get(i));
                    }
                    Node node = graphDatabaseService.createNode(labels);
                    String id  = nodeData.getId();
//                    if (StringUtils.isEmpty(id)) {
//                        id = String.valueOf((labels[1] + nodeData.getName() + taskId).hashCode());
//                    }
                    node.setProperty(GraphDbConstant.PROPERTY_KEY_ID, id);
                    Map<String, Object> props = nodeData.getProperties();
                    Optional.ofNullable(props).ifPresent(prop -> prop.forEach((k, v) -> {
                        if (!org.springframework.util.StringUtils.isEmpty(k) && !org.springframework.util.StringUtils.isEmpty(v)) {
                            if(k.equals(GraphDbConstant.PROPERTY_KEY_SOURCE)) {
                                node.setProperty(k, JSON.toJSONString(v));
                            } else {
//                                node.setProperty(k, v.toString());
                                node.setProperty(k, v);
                            }
                        }
                    }));
                    node.setProperty(GraphDbConstant.PROPERTY_KEY_TASK_ID, taskId);
                    mapNodes.put(nodeData.getId(), node);

                } else {
                    mapNodes.put(nodeData.getId(), existNode);
                }
                System.out.println("插入第 " + num[0] + " 个节点");
                num[0]++;
            });
            boolean hasRedisTask = false;
            if (RedisConstant.SAVE_FINISHED.equals(redisUtil.get(RedisConstant.KEY_PREFIX_TASK + taskId))) {
                hasRedisTask = true;
            }
            for (Graph.Link linkData : graphData.getLinks()) {
                boolean isExistLink = false;
                RelationshipType type = RelationshipType.withName(linkData.getType());
                String startNodeId = linkData.getStartNodeId();
                Node startNode = mapNodes.get(startNodeId);
                String endNodeId = linkData.getEndNodeId();
                Node endNode = mapNodes.get(endNodeId);
                if (null != startNode && null != endNode) {
                    String id = linkData.getId();
                    if (hasRedisTask) {
                        isExistLink = redisUtil.hasKey("relationship_" + taskId + "_" +  id);
                    } else {
                        for (Relationship relationship : startNode.getRelationships(type)) {
                            if (relationship.getAllProperties().containsKey(GraphDbConstant.PROPERTY_KEY_ID) &&
                                    relationship.getProperty(GraphDbConstant.PROPERTY_KEY_ID).equals(linkData.getId())) {
                                isExistLink = true;
                                break;
                            }
                        }
                    }

                    if (!isExistLink) {
                        org.neo4j.graphdb.Relationship relationship = startNode.createRelationshipTo(endNode, type);
//                        if (StringUtils.isEmpty(id)) {
//                            id = String.valueOf((type + startNodeId + endNodeId + taskId).hashCode());
//                        }
                        relationship.setProperty(GraphDbConstant.PROPERTY_KEY_ID, id);

                        Map<String, Object> props = linkData.getProperties();
                        Optional.ofNullable(props).ifPresent(prop -> prop.forEach((k, v) -> {
                            if (!org.springframework.util.StringUtils.isEmpty(k) && !org.springframework.util.StringUtils.isEmpty(v)) {
                                if (k.equals(GraphDbConstant.PROPERTY_KEY_SOURCE)) {
                                    relationship.setProperty(k, JSON.toJSONString(v));
                                } else {
                                    relationship.setProperty(k, v.toString());
                                }
                            }
                        }));
                        relationship.setProperty(GraphDbConstant.PROPERTY_KEY_TASK_ID, taskId);
                        taskGraphData.saveToRedis(RedisConstant.KEY_PREFIX_RELATIONSHIP + taskId + "_" +  id, linkData);
                    }
                }
            }
            tx.success();
        }
        return true;
    }

    public Set<Graph.Node> findNodes(String label, Map<String, Object> propertyValues) {
        Set<Graph.Node> retNodes = new LinkedHashSet<>();
        GraphDatabaseService graphDatabaseService = embeddedNeo4j.getGraphDatabaseService();
        try (Transaction tx = graphDatabaseService.beginTx()) {
            ResourceIterator<Node> nodes = graphDatabaseService.findNodes(Label.label(label), propertyValues);
            nodes.forEachRemaining(node -> {
                Graph.Node retNode = transferDbNodeToNode(node);
                retNodes.add(retNode);
            });
        }
        return retNodes;
    }

    public Set<Graph.Link> listRelationshipsByTaskId(Integer taskId) {
        Set<Graph.Link> retLinks = new HashSet<>();
        String cypher = "MATCH ()-[r]-() WHERE r.taskId = " + taskId + " RETURN r";
        GraphDatabaseService graphDatabaseService = embeddedNeo4j.getGraphDatabaseService();
        try (Transaction tx = graphDatabaseService.beginTx()) {
            Result result = graphDatabaseService.execute(cypher);
            result.forEachRemaining(res -> {
                Graph.Link link = new Graph.Link();
                Relationship r = (Relationship) res.get("r");
                link.setId(r.getProperty(GraphDbConstant.PROPERTY_KEY_ID).toString());
                retLinks.add(link);
            });
        }
        return retLinks;
    }

}


